import os
import resource
import subprocess
import time
from judge.crer.abstract_cr import CompileAndRuner
from utils.util import *

# C++编译运行器
class CppCR(CompileAndRuner):
    # 编译
    def _compile(self, file_name):
        # 执行编译逻辑
        try:
            # 创建子进程
            compile_err = PathUtil.compiler_error(file_name)
            args = ["g++", "-o", PathUtil.exe(file_name), PathUtil.src(file_name, ".cpp"), "-std=c++11"]
            with open(compile_err, 'w') as compile_err_file:
                subprocess.run(
                    args,
                    stderr = compile_err_file
                )

            # 检查编译是否成功
            if os.path.exists(PathUtil.exe(file_name)):
                print(PathUtil.src(file_name, ".cpp") + "编译成功!")
                return True
            else:
                print("编译失败，没有形成可执行程序")
                return False
            
        except Exception as e:
            print("编译失败:", e)
            return False
        
    # 设置limit
    def _set_proc_limit(self, cpu_limit, mem_limit):
         # cpp设置进程cpu和memory分配逻辑
        try:
            # 设置CPU时长（传入的是ms）
            resource.setrlimit(resource.RLIMIT_CPU, (max((cpu_limit / 1000.0), 1), resource.RLIM_INFINITY))
            # 设置内存大小（传入的是KB）
            resource.setrlimit(resource.RLIMIT_AS, (mem_limit * 1024 * 1024, resource.RLIM_INFINITY))
            print("进程资源限制设置成功")
        except Exception as e:
            print("设置进程资源限制失败:", e)

    # 运行
    def _run(self, file_name, cpu_limit, mem_limit):
        execute = PathUtil.exe(file_name)
        stdin = PathUtil.stdin(file_name)
        stdout = PathUtil.stdout(file_name)
        stderr = PathUtil.stderr(file_name)

        pid = os.fork()
        if pid == 0:
            # 子进程
            try:
                self._set_proc_limit(cpu_limit, mem_limit)
                
                # 重定向标准输入/输出/错误
                with open(stdin, 'w') as stdin_file:
                    os.dup2(stdin_file.fileno(), 0)
                with open(stdout, 'w') as stdout_file:
                    os.dup2(stdout_file.fileno(), 1)
                with open(stderr, 'w') as stderr_file:
                    os.dup2(stderr_file.fileno(), 2)
                os.execvp(execute, [execute])
            except Exception as e:
                print(f"子进程执行失败: {e}")
                os._exit(1)
        else:
            # 父进程
            start_time = time.time()
            _, status = os.waitpid(pid, 0)
            end_time = time.time()
            
            usage = resource.getrusage(resource.RUSAGE_CHILDREN)
            
            if os.WIFEXITED(status):
                exit_status = os.WEXITSTATUS(status)
            elif os.WIFSIGNALED(status):
                exit_status = os.WTERMSIG(status)
            else:
                exit_status = -1
            
            runtime = end_time - start_time
            max_memory = usage.ru_maxrss  # 最大驻留集大小，以 KB 为单位

            return {
                "exit_status": exit_status,
                "runtime": runtime,
                "max_memory": max_memory,
            }

    # 启动
    def start(self, code, cpu_limit, mem_limit):
        # 返回需要
        # status_code -- 状态码
        # status -- 状态信息
        
        # 生成唯一文件名
        unique_filename = FileUtil.unique_filename()
        to_end = False # 模拟goto语句
        
        run_result_time = "N/A"  # 初始化为 "N/A"
        run_result_memory = "N/A"  # 初始化为 "N/A"
        
        # 1. 代码为空
        if len(code) == 0:
            status_code = -1
            to_end = True

        # 2. 形成唯一临时文件失败
        if not to_end and not FileUtil.write_file(PathUtil.src(unique_filename, ".cpp"), code):
            status_code = -2  # 当做未知错误
            to_end = True

        # 3. 编译失败
        if not to_end and not self._compile(unique_filename):
            status_code = -3
            to_end = True

        # 4. 运行
        if not to_end:
            # 运行
            run_result = self._run(unique_filename, cpu_limit, mem_limit)
    
            run_result_time = "{:.2f}".format(run_result.get("runtime") * 1000)  # 单位ms
            run_result_memory = "{:.2f}".format(run_result.get("max_memory") / 1024.0)  # 单位MB
            
            run_result_status = run_result.get("exit_status")
            
            if run_result_status < 0:
                status_code = -2  # 未知错误
            elif run_result_status > 0:
                status_code = run_result_status  # 程序运行崩溃
            else:
                status_code = 0  # 运行成功
            to_end = True

        if to_end:
            if status_code != -3:  # 只有在编译成功后才更新 run_result_time 和 run_result_memory
                run_result_time = "N/A" if status_code == -2 else run_result_time
                run_result_memory = "N/A" if status_code == -2 else run_result_memory
            
            out_obj = {
                "status": status_code,
                "is_pass": False,
                "use_time": run_result_time,
                "use_memory": run_result_memory,
                "reason": CodeUtil.code_to_desc(status_code, unique_filename),
            }
            
            if status_code == 0:
                # 整个过程成功
                stdout = FileUtil.read_file(PathUtil.stdout(unique_filename), True)
                stderr = FileUtil.read_file(PathUtil.stderr(unique_filename), True)
                is_pass = all(substring in stdout for substring in ["测试用例1已通过", "测试用例2已通过", "测试用例3已通过"])
                success_json = {
                    "is_pass": is_pass,
                    "stdout": stdout,
                    "stderr": stderr,
                }
                out_obj.update(success_json)


        # 移除临时文件
        FileUtil.remove_tempfile(unique_filename, ".cpp")
        return out_obj
        
        

# Java编译运行器
class JavaCR(CompileAndRuner):
    def _compile(self, file_name):
        # java编译逻辑
        pass

    def _set_proc_limit(self, cpu_limit, mem_limit):
        # java设置进程cpu和memory分配逻辑
        pass

    # 运行
    def _run(self, file_name, cpu_limit, mem_limit):
        # java运行逻辑
        pass


# Python编译运行器
class PythonCR(CompileAndRuner):
    def _compile(self, file_name):
        # python不用编译，直接run
        pass

    def _set_proc_limit(self, cpu_limit, mem_limit):
        # python设置进程cpu和memory分配逻辑
        pass

    # 运行
    def _run(self, file_name, cpu_limit, mem_limit):
        # python运行逻辑
        pass



class CRFactory:
    @staticmethod
    def get_compile_and_run(language):
        if language == "C++":
            return CppCR()
        elif language == "Java":
            return JavaCR()
        elif language == "Python":
            return PythonCR()
        else:
            return CppCR()
